#include <ntddk.h>
#include <windef.h>

DWORD g_dwValue = 0;

VOID
FirstThread(PVOID Context);

VOID
SecondThread(PVOID Context);

VOID
DrvMultithreadUnload(PDRIVER_OBJECT DriverObject);

NTSTATUS
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegisterPath)
{
	NTSTATUS Status            = STATUS_SUCCESS;
	DriverObject->DriverUnload = DrvMultithreadUnload;

	HANDLE hThreads[2]     = { NULL };
	PVOID ThreadObjects[2] = { NULL };

	KEVENT Kevent;
	KeInitializeEvent(&Kevent, NotificationEvent, FALSE);

	Status = PsCreateSystemThread(&hThreads[0], THREAD_ALL_ACCESS, NULL, NULL, NULL, FirstThread, &Kevent);
	if (NT_SUCCESS(Status) == FALSE) {

		KdPrintEx((77, 0, "PsCreateSystemThread_1 Error: %x\n", Status));
		return Status;
	}

	Status = PsCreateSystemThread(&hThreads[1], THREAD_ALL_ACCESS, NULL, NULL, NULL, SecondThread, &Kevent);
	if (NT_SUCCESS(Status) == FALSE) {

		KdPrintEx((77, 0, "PsCreateSystemThread_2 Error: %x\n", Status));
		return Status;
	}

	__try {

		Status = ObReferenceObjectByHandle(hThreads[0], THREAD_ALL_ACCESS, *PsThreadType, KernelMode, &ThreadObjects[0], NULL);
		Status = ObReferenceObjectByHandle(hThreads[1], THREAD_ALL_ACCESS, *PsThreadType, KernelMode, &ThreadObjects[1], NULL);
	}
	__except (EXCEPTION_EXECUTE_HANDLER) {

		KdPrintEx((77, 0, "ObReferenceObjectByHandle Error: %x\n", Status));

		ZwClose(hThreads[0]);
		ZwClose(hThreads[1]);

		return Status;
	}

	Status = KeWaitForMultipleObjects(2, ThreadObjects, WaitAll, Executive, KernelMode, FALSE, NULL, NULL);
	if (NT_SUCCESS(Status) == FALSE) {

		KdPrintEx((77, 0, "KeWaitForMultipleObjects Error: %x\n", Status));
	}

	ObDereferenceObject(ThreadObjects[0]);
	ObDereferenceObject(ThreadObjects[1]);

	ZwClose(hThreads[0]);
	ZwClose(hThreads[1]);

	return Status;
}

VOID
DrvMultithreadUnload(PDRIVER_OBJECT DriverObject)
{

}

VOID
FirstThread(PVOID Context)
{
	for (DWORD dwI = 0; dwI < 100; dwI++) {

		g_dwValue += 1;
		if (g_dwValue % 2 == 0) {

			KeSetEvent((PRKEVENT)Context, IO_NO_INCREMENT, FALSE);
		}

		LARGE_INTEGER RelativeTime = { 0 };
		RelativeTime.QuadPart = -100 * 10 * 1000;
		KeDelayExecutionThread(KernelMode, FALSE, &RelativeTime);
	}
}

VOID
SecondThread(PVOID Context)
{
	while (g_dwValue <= 100) {

		KeWaitForSingleObject((PRKEVENT)Context, Executive, KernelMode, FALSE, NULL);
		KdPrintEx((77, 0, "The even values of g_dwValue: %d\n", g_dwValue));
		KeClearEvent((PRKEVENT)Context);
	}
}